<template>
  <video :src="video" controls />
  <br />
  <button @click="transcode" :disabled="isProcessing">
    {{ isProcessing ? 'Processing...' : 'Start Transcode' }}
  </button>
  <button @click="downloadVideo" :disabled="!video" class="ml-2">
    Download Video
  </button>
  <button @click="showFileList" :disabled="!isFfmpegLoaded" class="ml-2">
    Show Files
  </button>
  <div>
    <label>Quality (lower is better): <input type="range" v-model.number="quality" min="18" max="30" step="1" /></label>
    <span>{{ quality }}</span>
  </div>
  <div>
    <label>Resolution:
      <select v-model="resolution">
        <option value="1280:720">720p</option>
        <option value="854:480">480p</option>
        <option value="640:360">360p</option>
        <option value="426:240">240p</option>
      </select>
    </label>
  </div>
  <p>{{ message }}</p>
  <pre v-if="ffmpegLogs">{{ ffmpegLogs }}</pre>
  <pre v-if="fileList">{{ fileList }}</pre>
</template>

<script lang="ts">
import { FFmpeg } from '@ffmpeg/ffmpeg'
import type { LogEvent } from '@ffmpeg/ffmpeg/dist/esm/types'
import { fetchFile, toBlobURL } from '@ffmpeg/util'
import { defineComponent, ref } from 'vue'

const baseURL = 'https://cdn.jsdelivr.net/npm/@ffmpeg/core-mt@0.12.9/dist/esm'
const videoURL = 'https://oss.baiweicaijing.com/v3/uploads/video/2025/07/22/61491584d89669f2c53b39106ccc15478aff4d.mp4'

export default defineComponent({
  name: 'App',
  setup() {
    const ffmpeg = new FFmpeg()
    const message = ref('Click Start to Transcode')
    const video = ref('')
    const isProcessing = ref(false)
    const isFfmpegLoaded = ref(false)
    const ffmpegLogs = ref('')
    const fileList = ref('')
    const quality = ref(28) // 较高的CRF值表示更低的质量和更小的文件
    const resolution = ref('854:480') // 默认480p
    let downloadBlob: Blob | null = null

    async function transcode() {
      isProcessing.value = true
      message.value = 'Loading ffmpeg-core.js'
      ffmpegLogs.value = ''
      fileList.value = ''

      ffmpeg.on('log', ({ message: msg, type }: LogEvent) => {
        ffmpegLogs.value += `[${type}] ${msg}\n`
        if (type === 'error') {
          console.error('FFmpeg error:', msg)
        }
      })

      try {
        // 加载FFmpeg核心，增加内存限制
        if (!isFfmpegLoaded.value) {
          await ffmpeg.load({
            coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, 'text/javascript'),
            wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, 'application/wasm'),
            workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, 'text/javascript'),
            // 增加内存页大小（每页64KB）
            // 注意：过大的内存请求可能被浏览器拒绝
            wasmOptions: {
              // 初始内存：256MB (4096 pages * 64KB)
              // 最大内存：1GB (16384 pages * 64KB)
              initialMemory: 4096,
              maximumMemory: 16384
            }
          })
          isFfmpegLoaded.value = true
        }

        // 检查输入文件
        message.value = 'Checking input file'
        const response = await fetch(videoURL)
        if (!response.ok) {
          throw new Error(`Failed to fetch video: ${response.status} ${response.statusText}`)
        }

        // 分块下载视频以减少内存压力
        message.value = 'Fetching video data in chunks'
        const videoData = await fetchFile(videoURL)
        await ffmpeg.writeFile('input.mp4', videoData)

        // 验证输入文件
        const inputFile = await ffmpeg.readFile('input.mp4')
        if (inputFile.byteLength < 1024) {
          throw new Error(`Input file is too small (${inputFile.byteLength} bytes). Download failed.`)
        }

        // 使用优化的转码命令，降低内存需求
        message.value = 'Start transcoding with optimized settings'
        await ffmpeg.exec([
          '-i', 'input.mp4',
          '-c:v', 'libx264',
          '-crf', quality.value.toString(), // 使用用户选择的质量
          '-preset', 'ultrafast', // 使用最快的编码预设
          '-s', resolution.value, // 使用用户选择的分辨率
          '-threads', '1', // 限制线程数，减少内存压力
          '-tile-columns', '0', // 禁用 tiled encoding
          '-g', '30', // 增大GOP大小，减少I帧数量
          '-c:a', 'aac',
          '-b:a', '64k', // 降低音频比特率
          '-movflags', '+faststart',
          'output.mp4'
        ])

        // 验证输出文件
        const outputData = await ffmpeg.readFile('output.mp4')
        if (outputData.byteLength < 1024) {
          // 尝试更简单的转码方法
          message.value = 'Transcode attempt failed, trying alternative method'
          await ffmpeg.exec([
            '-i', 'input.mp4',
            '-c:v', 'libx264',
            '-crf', '30',
            '-preset', 'ultrafast',
            '-s', '640:360',
            '-c:a', 'aac',
            '-b:a', '64k',
            '-threads', '1',
            'output_simple.mp4'
          ])

          const simpleOutput = await ffmpeg.readFile('output_simple.mp4')
          if (simpleOutput.byteLength < 1024) {
            // 尝试仅提取视频流
            await ffmpeg.exec([
              '-i', 'input.mp4',
              '-c:v', 'copy',
              '-an', // 无音频
              'output_stream.mp4'
            ])

            const streamOutput = await ffmpeg.readFile('output_stream.mp4')
            if (streamOutput.byteLength < 1024) {
              throw new Error('All transcode methods failed.')
            }

            downloadBlob = new Blob([(streamOutput as Uint8Array).buffer], { type: 'video/mp4' })
            video.value = URL.createObjectURL(downloadBlob)
            message.value = 'Extracted video stream only! No audio. Click Download to save.'
            return
          }

          downloadBlob = new Blob([(simpleOutput as Uint8Array).buffer], { type: 'video/mp4' })
          video.value = URL.createObjectURL(downloadBlob)
          message.value = 'Transcoding completed using alternative method! Click Download to save.'
        } else {
          downloadBlob = new Blob([(outputData as Uint8Array).buffer], { type: 'video/mp4' })
          video.value = URL.createObjectURL(downloadBlob)
          message.value = 'Transcoding completed! Click Download to save.'
        }
      } catch (error) {
        console.error('Transcode error:', error)
        message.value = 'Error: ' + (error as Error).message
      } finally {
        // 释放FFmpeg资源
        try {
          await ffmpeg.exit()
        } catch (e) {
          console.warn('Error exiting FFmpeg:', e)
        }
        isProcessing.value = false
      }
    }

    function downloadVideo() {
      if (!downloadBlob) return

      const link = document.createElement('a')
      link.href = video.value
      link.download = 'converted-video.mp4'
      document.body.appendChild(link)
      link.click()
      document.body.removeChild(link)
    }

    async function showFileList() {
      if (!isFfmpegLoaded.value) return

      try {
        const files = await ffmpeg.execute(['-hide_banner', '-filters'])
        fileList.value = files.toString()
      } catch (error) {
        fileList.value = 'Error fetching file list: ' + (error as Error).message
      }
    }

    return {
      video,
      message,
      transcode,
      downloadVideo,
      isProcessing,
      ffmpegLogs,
      fileList,
      showFileList,
      isFfmpegLoaded,
      quality,
      resolution
    }
  }
})
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}

button {
  padding: 8px 16px;
  margin: 4px;
  background-color: #4CAF50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}

button:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

pre {
  text-align: left;
  margin: 10px auto;
  padding: 10px;
  background-color: #f5f5f5;
  border: 1px solid #ddd;
  border-radius: 4px;
  max-width: 800px;
  max-height: 300px;
  overflow: auto;
}

input, select {
  margin: 5px;
  padding: 5px;
}
</style>